#!/usr/bin/perl

#------------------------------------------------------------#
# Program to bin the SS433 photometric data in all_sort.dat  #
# in various ways.                                           #
#                                             Bmort  31Jan07 #
#------------------------------------------------------------#

system("clear");

print "===================================================\n";
print "\tData binning for SS433 data\n";
print "\t(for help see binning.readme)\n";
print "===================================================\n";

# GET COMMAND ARGS FOR FILE NAMES
$numArgs = $#ARGV + 1;
if ($numArgs != 2) {
    message("ERROR: Incorect number of command line args");
    messageLine();
    usage();
    exit(1);
}

$dataFileName = $ARGV[0];
$outFile = $ARGV[1];
message("Input data file = \"$dataFileName\"");
message("Output file root= \"$outFile\"");
messageLine();

# OPEN DATA FILE AND STORE IN ARRAY OF RAW DATA
use Cwd;
$dir = getcwd(); # Where am I?
open(DATA, $dataFileName);
@lines = <DATA>; # read data file into an array
close(DATA);

# EXTRACT RAW DATA INTO USEFUL ARRAYS
#====================================
$i=0;
$lskip = 0; # fix for blank lines
@ObsTime;
@ObsMag;
foreach $line (@lines) {
    if ($line =~ /^\s+/) { print "** "; $lskip++;}
    $line =~ m/^(\d+\.\d+|\d+)\s+(\d+\.\d+|\d+)/;
    $ObsTime[$i] = $1;
    $ObsMag[$i]  = $2;
    $i++;
}


# GET SOME INFO ON THE DATA
#==========================
$dataLength = $i-$lskip;
message("Data Length (# samples) = $dataLength");
$ObsTimeRange = $ObsTime[$dataLength-1]-$ObsTime[0];
message("Obs. time range ($ObsTime[0] to $ObsTime[$dataLength-1]) = $ObsTimeRange");
messageLine();

# GET BINNING PARAMETERS (FROM STDIN - USER PROMTEDED)
#=====================================================
$BinType = &promptUser("* Binning Type (1=simple, 2=adaptive), ",1);
#simple mode parameters
if ($BinType == 1) {
    #prompt user for inputs 
    messageLine();
    title_message("Simple Binning options:");
    $binLength = &promptUser("* Enter bin length (days), ",
			     int $ObsTimeRange/5);
    $binOverlap = &promptUser("* Enter bin overlap (days), ",0);
    $binStartOffset = &promptUser("* Enter bin start offset (days), ",0);
    #print summary
    message();
    sub_message("Bin length  = $binLength");
    sub_message("Bin overlap = $binOverlap");
    sub_message("Bin start offset = $binStartOffset");
} 
#adaptive mode parameters
elsif ($BinType == 2) { # adaptive
    $minBinLength = &promptUser("* Enter min bin length (days), ",100);

}
else {
    message("ERROR: Unrecognised binning type!");
    exit(2);
}

#check for some simple errors in imputs
if ($binOverlap >= $binLength) {
    messageLine();
    message("ERROR: Bin overlap longer than bin length, check options!");
    exit(3);
}



# (SIMPLE BINNING) GET NUMBER OF BINS
#====================================
$range = $ObsTimeRange-$binStartOffset+1;
$overlap = $binOverlap;
if ($BinType == 1) {
    $fractNumBins = ($range-$overlap)/($binLength-$overlap);
    $intNumBins = int $fractNumBins;
    messageLine();
    message("Fractional number of bins  = $fractNumBins");   
    message("Whole number of bins       = $intNumBins");   
    if ($intNumBins < 1) {
	message();
	message("ERROR: No full length bins fit in the data set, check options!");
	exit(3);
    }
    messageLine();
}

# (SIMPLE BINNING) GENERATE IDEAL BINS (NO RELATION TO DATA)
#===========================================================
if ($BinType == 1) {
@binFileNames;  # array holding the filenames of the bin files

@binStartTimes; # arrays to hold bin edge indices and times
@binEndTimes;

$iBin = 0; # bin index
$binStartTimes[$iBin] = $ObsTime[0]+$binStartOffset;
$binEndTimes[$iBin]   = $binStartTimes[$iBin]+($binLength-1);
for ($iBin=1;$iBin<$intNumBins;$iBin++) {
    $binStartTimes[$iBin] = $binEndTimes[$iBin-1]+1-$binOverlap;
    $binEndTimes[$iBin]   = $binStartTimes[$iBin]+($binLength-1);
}
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
    $binFileNames[$iBin] = "$outFile-bin".$iBin.".dat";
}
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
    title_message("Bin-$iBin:");
    sub_message("fileName = $binFileNames[$iBin]");
    sub_message("time     = $binStartTimes[$iBin] to $binEndTimes[$iBin]");
}
#messageLine();

}


# (SIMPLE BINNING) FIND REAL BINS
# ===============================
if ($BinType == 1) {
# the real bin time boundary arrays
@dataBinStartTimes;
@dataBinEndTimes;  
# find the real data times closest to @BinStartTimes
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
    $tmpBinStartTimeLower = 0.0;
    $tmpBinStartTimeUpper = 0.0;
    $diffLower = 0.0;
    $diffUpper = 0.0;
    $tmpBinStartIndexLower = 0;
    $tmpBinStartIndexUpper = 0;
    for ($i=0;$i<($dataLength);$i++) {
	if ($ObsTime[$i] <= $binStartTimes[$iBin]) {
	    $tmpBinStartTimeLower  = $ObsTime[$i];
	    $tmpBinStartTimeUpper  = $ObsTime[$i+1];
	    $tmpBinStartIndexLower = $i;
	    $tmpBinStartIndexUpper = $i+1;
	}
    }
    $diffLower = $binStartTimes[$iBin]-$tmpBinStartTimeLower;
    $diffUpper = $tmpBinStartTimeUpper -$binStartTimes[$iBin];
    if ($diffLower < $diffUpper) {
	$dataBinStartTimes[$iBin] = $tmpBinStartTimeLower;
    } else {
	$dataBinStartTimes[$iBin] = $tmpBinStartTimeUpper;
    }
    #message("Start - Bin-$iBin (differences)");
    #sub_message("L: $diffLower ($tmpBinStartTimeLower $tmpBinStartIndexLower)");
    #sub_message("U: $diffUpper ($tmpBinStartTimeUpper $tmpBinStartIndexUpper)");
}
# find the read data times closest to @BinEndTimes
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
    $tmpBinEndTimeLower = 0.0;
    $tmpBinEndTimeUpper = 0.0;
    $diffLower = 0.0;
    $diffUpper = 0.0;
    $tmpBinEndIndexLower = 0;
    $tmpBinEndIndexUpper = 0;
    #print "finding closest to $binEndTimes[$iBin]...\n";
    for ($i=0;$i<($dataLength);$i++) {
	if ($ObsTime[$i] <= $binEndTimes[$iBin]) {
	    $tmpBinEndTimeLower  = $ObsTime[$i];
	    $tmpBinEndTimeUpper  = $ObsTime[$i-1];
	    $tmpBinEndIndexLower = $i;
	    $tmpBinEndIndexUpper = $i-1;
	}
    }
    $diffLower = $binEndTimes[$iBin]-$tmpBinEndTimeLower;
    $diffUpper = $tmpBinEndTimeUpper -$binEndTimes[$iBin];
    if (abs($diffLower) < abs($diffUpper)) {
	$dataBinEndTimes[$iBin] = $tmpBinEndTimeLower;
    } else {
	$dataBinEndTimes[$iBin] = $tmpBinEndTimeUpper;
    }
#    message("End - Bin-$iBin (differences)");
#    sub_message("L: $diffLower ($tmpBinStartTimeLower $tmpBinStartIndexLower)");
#    sub_message("U: $diffUpper ($tmpBinStartTimeUpper $tmpBinStartIndexUpper)");
}
messageLine();
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
    message("Bin-$iBin");
    sub_message("startTime:  $dataBinStartTimes[$iBin]");
    sub_message("endTime:    $dataBinEndTimes[$iBin]");
}

}
messageLine();

# (SIMPLE BINNING) OUTPUT TO FILE
# =============================== 

# write the input data back to file (for checking)
title_message("Writing $outFile.indata");
open(OUT_INDATA, ">$outFile.indata");
for ($i=0;$i<($dataLength);$i++) {
    print OUT_INDATA "$ObsTime[$i]\t $ObsMag[$i]\n";
}
close(OUT_INDATA);

# write the info file on data and binning
title_message("Writing $outFile.info");
open(OUT_INFO, ">$outFile.info");
print OUT_INFO "RUNDIR      =        $dir\n";
print OUT_INFO "OUTFILE     =        $dataFileName\n";
print OUT_INFO "DATAFILE    =        $outFile\n";
print OUT_INFO "DATALENGTH  =        $dataLength\n";
print OUT_INFO "OBS_T_RANGE =        $ObsTimeRange\n";
if ($BinType == 1) {
print OUT_INFO "BINTYPE     =        Simple binning\n";
print OUT_INFO "LENGTH      =        $binLength\n";
print OUT_INFO "OVERLAP     =        $binOverlap\n";
print OUT_INFO "OFFSET      =        $binStartOffset\n";
print OUT_INFO "FRACBINS    =        $fractNumBins\n";
print OUT_INFO "INTBINS     =        $intNumBins\n";
print OUT_INFO "TOT_OVERLAP =        $overlaps\n";
}
if ($BinType == 2) {
print OUT_INFO "BINTYPE     =        Adaptive\n";
}
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
print OUT_INFO "#BIN        =        Bin-$iBin:\n";
print OUT_INFO "FILENAME    =        $binFileNames[$iBin]\n";
print OUT_INFO "START_TIME  =        $dataBinStartTimes[$iBin]\n";
print OUT_INFO "END_TIME    =        $dataBinEndTimes[$iBin]\n";
}
close(OUT_INFO);



# write a summary file with the input data and bin start/end flags
$iBin=0;
title_message("Writing $outFile.summary");
open(OUT_SUMMARY, ">$outFile.summary");
for ($i=0;$i<($dataLength);$i++) {
    if ($ObsTime[$i] == $dataBinStartTimes[$iBin]) {
	print OUT_SUMMARY "$ObsTime[$i]\t $ObsMag[$i]   <== START ($iBin)\n";
    }
    if ($ObsTime[$i] == $dataBinEndTimes[$iBin]) {
	print OUT_SUMMARY "$ObsTime[$i]\t $ObsMag[$i]   <== END ($iBin)\n";
	$iBin++;
    }
    else {
	print OUT_SUMMARY "$ObsTime[$i]\t $ObsMag[$i]\n";
    }
}
close(OUT_SUMMARY);
messageLine();

# write the bin data
for ($iBin=0;$iBin<$intNumBins;$iBin++) {
    title_message("Writing $binFileNames[$iBin]");
    open(OUT_BIN, ">$binFileNames[$iBin]");
    for ($i=0;$i<($dataLength);$i++) {
	if($ObsTime[$i] >= $dataBinStartTimes[$iBin] &&
	   $ObsTime[$i] <= $dataBinEndTimes[$iBin]) {
		print OUT_BIN "$ObsTime[$i]  $ObsMag[$i]\n";
	}
    }
    #message("done!");
    close(OUT_BIN);
}
messageLine();




#################################################

sub usage()
{
    print "-\n";
    print "- USAGE: []\$ perl dataBinning.pl [DATAFILENAME] [OUTNAME_ROOT]\n";
    print "-\n";
}

sub message {
    my $messageString = shift;
    print "- $messageString\n";
}

sub messageLine {
    print "---------------------------------------------------\n";
}


sub sub_message {
    my $messageString = shift;
    print "-   $messageString\n";
}

sub title_message {
    my $messageString = shift;
    print "= $messageString\n";
}


sub promptUser {
    
    #-------------------------------------------------------------------#
    #  two possible input arguments - $promptString, and $defaultValue  #
    #  make the input arguments local variables.                        #
    #-------------------------------------------------------------------#
    
    local($promptString,$defaultValue) = @_;
    
    #-------------------------------------------------------------------#
    #  if there is a default value, use the first print statement; if   #
    #  no default is provided, print the second string.                 #
    #-------------------------------------------------------------------#
    
    
    if ($defaultValue || $defaultValue == 0) {
	print $promptString, "[", $defaultValue, "]: ";
    } else {
	print $promptString, ": ";
    }

    $| = 1;               # force a flush after our print
    $_ = <STDIN>;         # get the input from STDIN (presumably the keyboard)


    #------------------------------------------------------------------#
    # remove the newline character from the end of the input the user  #
    # gave us.                                                         #
    #------------------------------------------------------------------#

    chomp;

    #-----------------------------------------------------------------#
    #  if we had a $default value, and the user gave us input, then   #
    #  return the input; if we had a default, and they gave us no     #
    #  no input, return the $defaultValue.                            #
    #                                                                 # 
    #  if we did not have a default value, then just return whatever  #
    #  the user gave us.  if they just hit the <enter> key,           #
    #  the calling routine will have to deal with that.               #
    #-----------------------------------------------------------------#

    if ("$defaultValue" || $defaultValue == 0) {
	return $_ ? $_ : $defaultValue;    # return $_ if it has a value
    } else {
	return $_;
    }
}


